<?php
namespace Alipay;
use Alipay;
require_once 'Encrypt.php';
final class Auth
{
    //应用ID
    public $APPID;
    //私钥文件路径
    public $rsaPrivateKeyFilePath;
    //私钥值
    public $APP_PRIVATE_KEY;

    public $ALIPAY_PUBLIC_KEY;
    //网关
    public $API_URL = "https://openapi.alipay.com/gateway.do";
    //返回数据格式
    public $format = "json";
    //api版本
    public $apiVersion = "1.0";
    public $CHARSET    = 'UTF-8';
    //签名类型
    public $signType = "RSA2";
    private	$media_id = "L21pZnMvVDFQV3hYWGJKWFhYYUNucHJYP3Q9YW13ZiZ4c2lnPTU0MzRhYjg1ZTZjNWJmZTMxZGJiNjIzNDdjMzFkNzkw575";
    private $SIGN_NODE_NAME = "sign";



    // 表单提交字符集编码
    public $postCharset = "UTF-8";
    //使用文件读取文件格式，请只传递该值
    public $alipayPublicKey = null;
    //使用读取字符串格式，请只传递该值
    public $alipayrsaPublicKey;
    public $debugInfo = false;
    private $fileCharset = "UTF-8";
    private $RESPONSE_SUFFIX = "_response";
    private $ERROR_RESPONSE = "error_response";

    //加密XML节点名称
    private $ENCRYPT_XML_NODE_NAME = "response_encrypted";
    private $needEncrypt = false;

    //加密密钥和类型
    public $encryptKey;
    public $encryptType = "AES";
    protected $alipaySdkVersion = "alipay-sdk-php-20161101";

    private $_userParam;
    protected $getUrl='';
    public $postData = array();

    public function __construct($APPID,$APP_PRIVATE_KEY,$ALIPAY_PUBLIC_KEY)
    {
        $this->APPID            = $APPID;
        $this->APP_PRIVATE_KEY  = $APP_PRIVATE_KEY;
        $this->ALIPAY_PUBLIC_KEY  = $ALIPAY_PUBLIC_KEY;
    }
    public function __set ($name, $value){
        $this->_userParam[$name] = $value;
    }
    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    function characet($data, $targetCharset) {

        if (!empty($data)) {
            $fileType = $this->fileCharset;
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
                //				$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
            }
        }
        return $data;
    }
    public function generateSign($params, $signType = "RSA2") {
        return $this->sign($this->getSignContent($params), $signType);
    }
    public function getSignContent($params) {
        ksort($params);// print_r($params);exit;
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                // 转换成目标字符集
                $v = $this->characet($v, $this->postCharset);
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }

        unset ($k, $v);
        //$stringToBeSigned = htmlspecialchars($stringToBeSigned, ENT_QUOTES | ENT_SUBSTITUTE, $this->postCharset, true);
        return $stringToBeSigned;
    }
    public function rsaSign($params, $signType = "RSA2") {
        return $this->sign($this->getSignContent($params), $signType);
    }
    /**
     * 校验$value是否非空
     *  if not set ,return true;
     *    if is null , return true;
     **/
    protected function checkEmpty($value) {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;

        return false;
    }
    protected function sign($data, $signType = "RSA2") {
        if($this->checkEmpty($this->rsaPrivateKeyFilePath)){ //ALIPAY_PUBLIC_KEY APP_PRIVATE_KEY
            $priKey=$this->APP_PRIVATE_KEY;
            $res = "-----BEGIN RSA2 PRIVATE KEY-----\n" .
                wordwrap($priKey, 64, "\n", true) .
                "\n-----END RSA2 PRIVATE KEY-----";
        }else {
            $priKey = file_get_contents($this->rsaPrivateKeyFilePath);
            $res = openssl_get_privatekey($priKey);
        }

        ($res) or die('您使用的私钥格式错误，请检查RSA私钥配置');

        if ("RSA2" == $signType) {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }

        if(!$this->checkEmpty($this->rsaPrivateKeyFilePath)){
            openssl_free_key($res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }




    protected function system(){
        return [
            'app_id'        =>  $this->APPID, //支付宝分配给开发者的应用ID
            'format'        =>  $this->format, //仅支持JSON
            'charset'       =>  $this->CHARSET, //编码格式
            'sign_type'     =>  $this->signType, //签名算法类型
            'timestamp'     =>  date('Y-m-d H:i:s'), //格式"yyyy-MM-dd HH:mm:ss"
            'version'       =>  $this->apiVersion, //API版本
            //'biz_content'   => '{"mediaId":"'. $this -> media_id  .'"}',
        ];
    }
    static public function createStrParam ($paramArr)
    {
        $strParam = array();
        foreach ($paramArr as $key => $val) {
            if ($key != '' && $val != '') {
                $strParam []= $key . '=' . urlencode($val);
            }
        }
        $url = implode('&',$strParam);
        return $url;
    }
    protected function signConfig(){
        $data2 	        = 	self::system();
        $data	        =	array_merge($data2,$this->_userParam);


        /*if (method_exists($request,"getNeedEncrypt") &&$request->getNeedEncrypt()){

            $sysParams["encrypt_type"] = $this->encryptType;

            if ($this->checkEmpty($apiParams['biz_content'])) {

                throw new Exception(" api request Fail! The reason : encrypt request is not supperted!");
            }

            if ($this->checkEmpty($this->encryptKey) || $this->checkEmpty($this->encryptType)) {

                throw new Exception(" encryptType and encryptKey must not null! ");
            }

            if ("AES" != $this->encryptType) {

                throw new Exception("加密类型只支持AES");
            }

            // 执行加密
            $enCryptContent = encrypt($apiParams['biz_content'], $this->encryptKey);
            $apiParams['biz_content'] = $enCryptContent;

        }*/

//待签名字符串
        //$preSignStr = $this->getSignContent($data);
        //签名
        $data["sign"] = $this->generateSign($data, $this->signType);

        //print_r($data);exit;
        return $data;
    }
    protected  function Url($mode){
        $data = $this->signConfig();
        //print_r($mode);exit;
        if($mode=='GET'){
        $strParam       = $this->createStrParam($data);
        $this->getUrl      = $this->API_URL.'?' . $strParam;
        }else{
            $this->getUrl      = $this->API_URL;
            $this->postData    =$data;
        }
    }

    public function Send ($mode = 'GET', $format = 'json'){
        $mode =  strtoupper($mode);
        $this->format = strtolower($format);
        $this->Url($mode);
        $data           =   new \Common\Extend\Api\GetData();//echo htmlspecialchars($this->getUrl, ENT_QUOTES | ENT_SUBSTITUTE, $this->postCharset, true);exit;
        $data->Mode     = $mode;
        $data->Url      = $this->getUrl;
        $data->format   = $format;
        $data->Charset  = $this->CHARSET;//print_r($this->getUrl);exit;
        $data->postData = $this->postData;
        return $data;
    }

    //支付宝提交
    /*
     Array
        (
            [biz_content] => {"productCode":"QUICK_WAP_PAY","body":"购买测试商品0.01元","subject":"测试","out_trade_no":"201782111929858","total_amount":"0.01","timeout_express":"1m"}
            [app_id] => 2017060107400865
            [version] => 1.0
            [format] => json
            [sign_type] => RSA2
            [method] => alipay.trade.wap.pay
            [timestamp] => 2017-08-02 11:24:22
            [alipay_sdk] => alipay-sdk-php-20161101
            [terminal_type] =>
            [terminal_info] =>
            [prod_code] =>
            [notify_url] => http://工程公网访问地址/alipay.trade.wap.pay-PHP-UTF-8/notify_url.php
            [return_url] => http://mitsein.com/alipay.trade.wap.pay-PHP-UTF-8/return_url.php
            [charset] => UTF-8
            [sign] => Cy02OzsxQLe2ojDx0b1XC7NliOoju07yZVN5wSGYYMLEn8o5bs2BE+ptS28kNxeG0xEnWe4kvoErkU3SqwYQBOj+fkwv5OKSiTiodzTNqirjg2+zG7dwd5dHSd5OjVS3b8W+tRxSkFTcmCg7U3RRSCTfarhy1XoS64Po1yyNlg8=
        )
     */
    public function form($mode = 'POST', $format = 'json'){
        $mode =  strtoupper($mode);
        $this->format = strtolower($format);
        $this->Url($mode);
        $para_temp = $this->postData;
        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->getUrl."?charset=".trim($this->postCharset)."' method='POST'>";
        while (list ($key, $val) = each ($para_temp)) {
            if (false === $this->checkEmpty($val)) {
                $val = str_replace("'","&apos;",$val);
                $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
            }
        }
        $sHtml = $sHtml."<input type='submit' value='ok' style='display:none;''></form>";
        $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
        $data           =   new \Common\Extend\Api\GetData();
        $data->postData = $sHtml;
        return $data;
    }
    function check($arr){
        $result = $this->rsaCheckV1($arr, $this->signType);
        return $result;
    }



    /** rsaCheckV1 & rsaCheckV2
     *  验证签名
     *  在使用本方法前，必须初始化AopClient且传入公钥参数。
     *  公钥是否是读取字符串还是读取文件，是根据初始化传入的值判断的。
     **/
    public function rsaCheckV1($params,$signType='RSA2') {
        $sign = $params['sign'];
        $params['sign_type'] = null;
        $params['sign'] = null;

        return $this->verify($this->getSignContent($params), $sign,$signType);
    }
    public function rsaCheckV2($params, $signType='RSA2') {
        $sign = $params['sign'];
        $params['sign'] = null;
        return $this->verify($this->getSignContent($params), $sign, $signType);
    }

    function verify($data, $sign, $signType = 'RSA2') {
        if($this->checkEmpty($this->rsaPrivateKeyFilePath)){
            $pubKey= $this->ALIPAY_PUBLIC_KEY;
            $res = "-----BEGIN PUBLIC KEY-----\n" .
                wordwrap($pubKey, 64, "\n", true) .
                "\n-----END PUBLIC KEY-----";
        }else {
            //读取公钥文件
            $pubKey = file_get_contents($this->rsaPrivateKeyFilePath);
            //转换为openssl格式密钥
            $res = openssl_get_publickey($pubKey);
        }
        ($res) or die('支付宝RSA公钥错误。请检查公钥文件格式是否正确');
        //调用openssl内置方法验签，返回bool值
        if ("RSA2" == $signType) {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
        } else {
            $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        }//print_r($result);exit;
        if(!$this->checkEmpty($this->rsaPrivateKeyFilePath)) {
            //释放资源
            openssl_free_key($res);
        }
        return $result;
    }
    //请确保项目文件有可写权限，不然打印不了日志。
    function writeLog($text) {
        // $text=iconv("GBK", "UTF-8//IGNORE", $text);
        //$text = characet ( $text );
        $dir =  dirname ( __FILE__ ).DIRECTORY_SEPARATOR."./log.txt";
        file_put_contents ( $dir, date ( "Y-m-d H:i:s" ) . "  " . $text . "\r\n", FILE_APPEND );
    }
}
